Skip to content

chore: sync upstream v2026.3.8 with fork customizations (TDD verified) - #8

Open
planfit-alan wants to merge 8655 commits into
mainfrom
feature/upstream-rebase-v2026.3.8
Open

planfit-alan wants to merge 8655 commits into
mainfrom
feature/upstream-rebase-v2026.3.8

Conversation

@planfit-alan

Copy link
Copy Markdown
Collaborator

Summary

포크 기준 v2026.2.9 → upstream v2026.3.8 동기화 (6개 릴리즈)

🔴 보안 패치 (Critical)

upstream에 포함된 보안 수정 35+건:

  • Exec 승인 우회 차단, Browser SSRF 강화
  • Gateway 인증 취약점 수정, Workspace FS 경계 우회 차단
  • Config validation fail-closed, 의존성 보안 패치 (Hono 등)

새로 적용된 포크 커스텀 (TDD)

  • Subagent progress: Slack emoji reactions, 3분 stall detection, suppressChannelRelay
  • Cron: targetSessionKey for main jobs
  • Compaction: infinite cycle fix
  • Claude SDK 통합 (claude-code-tool 등)

⚠️ BREAKING Changes

  • gateway.auth.mode 명시 필요 (token+password 동시 설정 시)
  • ACP dispatch 기본 활성화
  • tools.profile 새 설치 기본값 messaging 변경

테스트 결과

  • pnpm build: PASS
  • pnpm test: 7155 passed / 1 unrelated upstream failure

vincentkoc and others added 30 commits March 8, 2026 11:17
Merged via squash.

Prepared head SHA: 62de5d5
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
jun-planfit and others added 30 commits March 10, 2026 11:53
…ype changes

When the config's defaultProvider type (cli vs embedded) differs from
the stored session providerOverride, the override is now automatically
reset to follow the config. This fixes the issue where changing config
from anthropic to claude-cli had no effect because sessions retained
the old providerOverride=anthropic.

Also removes all diagnostic debug logs ([routing], [timing], sdk event,
cli enqueue/dequeue, debugFile) added during investigation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…history

CLI provider writes to its own session but not to the OpenClaw JSONL
transcript, so the web UI chat.history shows no messages. Add
mirrorCliTurnToTranscript() that writes both user and assistant entries
to the session JSONL after each CLI turn completes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the proxy model (entire conversation through Claude Code) with a tool
model where the embedded Pi agent (Opus) is the primary agent and Claude Code
is a coding tool. This enables OpenClaw's tool ecosystem, policy controls, and
eliminates unnecessary Claude Code calls for general conversation.

- Add SDKProgressEvent type and onProgress callback to runSDKAgent
- Create claude-code-tool.ts with createClaudeCodeTool() AgentTool
- Register claude_code tool in pi-tools.ts (enabled by default)
- Add claudeCode config to ToolsConfig for model/maxTurns overrides

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Plan mode lets the caller request planning-only execution (no tool runs),
returning a sessionId that can be resumed to execute the plan.

3-tier permission: rule-based auto-allow/deny → gateway approval (Jun) → fallback allow.
Replaces hardcoded bypassPermissions with configurable permissionMode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
details object is not visible to the calling agent (Alan) — only
content text is returned as tool result. Append sessionId to the
text body so the agent can extract and use it for resume.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resume executes an already-reviewed plan, so bypass all permission
checks. 3-tier canUseTool is only needed for direct execute (new tasks).

- plan: permissionMode="plan", no canUseTool
- resume: permissionMode="bypassPermissions", no canUseTool
- execute: permissionMode="acceptEdits", canUseTool active

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
acceptEdits + canUseTool causes ZodError in SDK's internal validation
for both resume and direct execute modes. Use bypassPermissions for
all non-plan modes. Plan mode remains the primary safety gate.

buildCanUseTool preserved (as _buildCanUseTool) for future SDK versions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SDK tool_use content blocks contain input fields (e.g. path for Read)
that were being discarded during type casting. Now preserved and
forwarded via emitAgentEvent so resolveToolDisplay can render
"📖 Read: ~/workspace/file.ts" instead of just "📖 Read".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude Code SDK uses `file_path` key but resolveReadDetail/resolveWriteDetail
only checked `args.path`, causing paths to not display for SDK tool events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updated the default value for cron lane concurrency from 1 to 10 to better
support concurrent cron job execution. This change affects both the initial
gateway lane configuration and the config reload handler.

Added comprehensive test coverage for the cron lane concurrency defaults to
ensure proper behavior when the value is unset, explicitly configured, or
set to edge cases like 0.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
When an agent config specifies `model: { primary: "..." }` without
an explicit `fallbacks` array, the existing code replaced the entire
model object from `agents.defaults`—discarding the default fallbacks.

This caused cron jobs (and agent sessions) to have only one model
candidate (the pinned model) plus the global primary as a final
fallback, skipping all intermediate fallback models.

The fix merges the agent model override into the existing defaults
model object using spread, so that keys like `fallbacks` survive
when the agent only overrides `primary`. Agents can still explicitly
override or clear fallbacks by providing their own `fallbacks` array.

Reproduction scenario:
- `agents.defaults.model = { primary: "codex", fallbacks: ["opus", "flash", "deepseek"] }`
- Agent config: `model: { primary: "codex" }`
- Cron job pins: `model: "flash"`
- Before fix: fallback candidates = [flash, codex] (3 models lost)
- After fix: fallback candidates = [flash, opus, deepseek, ..., codex]
- Import ChatStreamer from @slack/web-api/dist/chat-stream.js (not re-exported from index)
- Fix TypeScript control flow narrowing for streamSession used in closure
When a thread session starts with no accumulated history, fetch prior
thread messages via conversations.replies. This gives the thread
session context of all previous messages including bot replies that
were generated by the parent channel session.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ipt split

When sessions_send triggers the gateway agent handler, the nextEntry was
missing sessionFile, causing the session store to lose the topic-based
transcript path and fall back to a plain {sessionId}.jsonl file instead
of the correct {sessionId}-topic-{threadTs}.jsonl file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dcoding webchat

When sending messages to Slack thread sessions via sessions_send, the
gateway was using channel="webchat" causing the target session's tool
context to lose Slack targeting. Now parses the channel from the session
key so Slack sessions correctly receive channel="slack".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add instruction to always check available_skills first even when a
native tool exists, ensuring skill-defined patterns and procedures
are followed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The parseDurationMs regex does not accept a leading +, which is
intentional since + is only meaningful in the --at context (meaning
"from now"). Strip the prefix in parseAt before delegating to
parseDurationMs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…m rebase

- Restore subagent-progress.ts and test file (deleted during rebase)
- Add resolveRunIdBySessionKey function and runIdBySessionKey map to agent-events.ts
- Add missing emitAgentEvent import to claude-code-tool.ts
- Remove HistoryFirstOnly references from get-reply-run.ts (deprecated)
- Remove enableProgress field from sessions-spawn-tool.ts (not in interface)
- Add steerMessage to maybeQueueSubagentAnnounce call in subagent-progress.ts
- Resolve merge conflict in agent-runner-execution.ts
Add threadId field to CronDeliverySharedProperties to enable thread-specific
cron delivery targeting. This preserves upstream's accountId and failureDestination
features while adding fork's threadId customization.
…and suppressChannelRelay

TDD Round 1: Subagent Progress Enhancements

Changes applied from origin/main:
- Slack emoji reactions (⏳ → ✅/❌) for lifecycle events
- 3-minute stall detection with ⏱️ warning reaction
- suppressToolSummaries flag for group chats and native commands
- Tool activity tracking with lastToolEventAt timestamps
- Lifecycle event handling (start/end/error)
- parentTool field filtering for SDK internal tools

Supporting changes:
- Updated AgentRunContext: replaced isControlUiVisible with suppressToolSummaries
- Added resolveRunIdBySessionKey and getAgentRunContext to agent-events
- Updated emitAgentEvent to use simplified sessionKey resolution
- Removed unused shouldSurfaceToControlUi variables and imports

Tests:
- All 26 subagent-progress tests pass (RED → GREEN)
- Build verified successful
- Test coverage includes emoji reactions, stall detection, lifecycle events

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Call conversations.typing API on each typing loop tick
- Ensures typing indicator is visible during tool execution (not just LLM streaming)
- Reduce default typingIntervalSeconds from 6 to 4 to prevent 1s gap with Slack's 5s TTL

Cherry-picked from origin/main commit 2ec6138

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…hanges

- Adjust expected conversations.replies call counts in thread tests
- Upstream now makes 3 calls for new threads (starter + accumulated + context)
  vs 2 calls in origin/main (starter + context only)
- Existing session test expects 2 calls (starter + accumulated) vs 1 in origin

All 389 Slack tests pass. Build verified.
Complete verification that all 15 target Slack commits from origin/main
are already integrated in upstream v2026.3.8. No porting work needed.
Round 3 (Cron) + Round 4 (Error Recovery) improvements from fork:

**targetSessionKey (High Priority #1)**
- Add targetSessionKey field to CronJob for routing main session systemEvents to specific threads/channels
- Validation: only allowed for sessionTarget="main" and wakeMode="now"
- Pass targetSessionKey to enqueueSystemEvent and runHeartbeatOnce
- Schema updates (types, TypeBox validators, CLI)
- 4 new tests (all passing)

**Compaction infinite cycle fix (Medium Priority #5)**
- Fix stale totalTokens → memory flush → auto-compaction infinite loop
- Accept tokensAfter >= 0 in incrementCompactionCount (was > 0)
- Ensures post-compaction token counts are correctly updated to 0

Upstream already has:
- MIN_REFIRE_GAP_MS spin loop prevention
- Concurrent execution with Promise.all
- Usage telemetry (CronUsageSummary)
- threadId on CronDelivery

Tests: 4 new passing, build passing, no regressions

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…sanitization bypass

TDD Round 5-7 implementation: Add google-antigravity opus 4.6 forward-compat.

Two fixes for Google Antigravity (Cloud Code Assist) reliability:

1. Forward-compat model fallback: pi-ai's model registry doesn't include
   claude-opus-4-6-thinking. Add resolveAntigravityOpus46ForwardCompatModel()
   that clones the opus-4-5 template so the correct api ("google-gemini-cli")
   and baseUrl are preserved.

2. Fix thinking.signature rejection: The API returns Claude thinking blocks
   without signatures, then rejects them on replay. Add sanitizeAntigravityThinkingBlocks
   which strips unsigned blocks. Apply it in the orphaned-user-message path
   in attempt.ts to prevent signature validation errors.

Changes:
- model-forward-compat.ts: Add resolveAntigravityOpus46ForwardCompatModel()
- google.ts: Export sanitizeAntigravityThinkingBlocks() with signature validation
- attempt.ts: Apply thinking block sanitization for orphaned messages
- transcript-policy.ts: Add normalizeAntigravityThinkingBlocks policy flag
- model.test.ts: Add test for antigravity opus 4.6 forward-compat fallback

Test coverage: All model tests passing (36/36), build successful

Upstream ref: b48ab77
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.